///*
// * Copyright (C) 2016 The Android Open Source Project
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// *      http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package com.android.apksig.internal.apk.v1;
//
//import com.android.apksig.ApkVerifier.Issue;
//import com.android.apksig.ApkVerifier.IssueWithParams;
//import com.android.apksig.apk.ApkFormatException;
//import com.android.apksig.apk.ApkUtils;
//import com.android.apksig.internal.jar.ManifestParser;
//import com.android.apksig.internal.util.AndroidSdkVersion;
//import com.android.apksig.internal.util.InclusiveIntRange;
//import com.android.apksig.internal.util.MessageDigestSink;
//import com.android.apksig.internal.zip.CentralDirectoryRecord;
//import com.android.apksig.internal.zip.LocalFileRecord;
//import com.android.apksig.util.DataSource;
//import com.android.apksig.zip.ZipFormatException;
//import java.io.IOException;
//import java.nio.ByteBuffer;
//import java.nio.ByteOrder;
//import java.security.MessageDigest;
//import java.security.NoSuchAlgorithmException;
//import java.security.SignatureException;
//import java.security.cert.CertificateException;
//import java.security.cert.X509Certificate;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.Base64;
//import java.util.Base64.Decoder;
//import java.util.Collection;
//import java.util.Collections;
//import java.util.HashMap;
//import java.util.HashSet;
//import java.util.List;
//import java.util.Locale;
//import java.util.Map;
//import java.util.Set;
//import java.util.StringTokenizer;
//import java.util.jar.Attributes;
//import sun.security.pkcs.PKCS7;
//import sun.security.pkcs.SignerInfo;
//
///**
// * APK verifier which uses JAR signing (aka v1 signing scheme).
// *
// * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">Signed JAR File</a>
// */
//public abstract class V1SchemeVerifier {
//
//    private static final String MANIFEST_ENTRY_NAME = V1SchemeSigner.MANIFEST_ENTRY_NAME;
//
//    private V1SchemeVerifier() {}
//
//    /**
//     * Verifies the provided APK's JAR signatures and returns the result of verification. APK is
//     * considered verified only if {@link Result#verified} is {@code true}. If verification fails,
//     * the result will contain errors -- see {@link Result#getErrors()}.
//     *
//     * @throws ApkFormatException if the APK is malformed
//     * @throws IOException if an I/O error occurs when reading the APK
//     * @throws NoSuchAlgorithmException if the APK's JAR signatures cannot be verified because a
//     *         required cryptographic algorithm implementation is missing
//     */
//    public static Result verify(
//            DataSource apk,
//            ApkUtils.ZipSections apkSections,
//            Map<Integer, String> supportedApkSigSchemeNames,
//            Set<Integer> foundApkSigSchemeIds,
//            int minSdkVersion,
//            int maxSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException {
//        if (minSdkVersion > maxSdkVersion) {
//            throw new IllegalArgumentException(
//                    "minSdkVersion (" + minSdkVersion + ") > maxSdkVersion (" + maxSdkVersion
//                            + ")");
//        }
//
//        Result result = new Result();
//
//        // Parse the ZIP Central Directory and check that there are no entries with duplicate names.
//        List<CentralDirectoryRecord> cdRecords = parseZipCentralDirectory(apk, apkSections);
//        Set<String> cdEntryNames = checkForDuplicateEntries(cdRecords, result);
//        if (result.containsErrors()) {
//            return result;
//        }
//
//        // Verify JAR signature(s).
//        Signers.verify(
//                apk,
//                apkSections.getZipCentralDirectoryOffset(),
//                cdRecords,
//                cdEntryNames,
//                supportedApkSigSchemeNames,
//                foundApkSigSchemeIds,
//                minSdkVersion,
//                maxSdkVersion,
//                result);
//
//        return result;
//    }
//
//    /**
//     * Returns the set of entry names and reports any duplicate entry names in the {@code result}
//     * as errors.
//     */
//    private static Set<String> checkForDuplicateEntries(
//            List<CentralDirectoryRecord> cdRecords, Result result) {
//        Set<String> cdEntryNames = new HashSet<>(cdRecords.size());
//        Set<String> duplicateCdEntryNames = null;
//        for (CentralDirectoryRecord cdRecord : cdRecords) {
//            String entryName = cdRecord.getName();
//            if (!cdEntryNames.add(entryName)) {
//                // This is an error. Report this once per duplicate name.
//                if (duplicateCdEntryNames == null) {
//                    duplicateCdEntryNames = new HashSet<>();
//                }
//                if (duplicateCdEntryNames.add(entryName)) {
//                    result.addError(Issue.JAR_SIG_DUPLICATE_ZIP_ENTRY, entryName);
//                }
//            }
//        }
//        return cdEntryNames;
//    }
//
//    /**
//     * All JAR signers of an APK.
//     */
//    private static class Signers {
//
//        /**
//         * Verifies JAR signatures of the provided APK and populates the provided result container
//         * with errors, warnings, and information about signers. The APK is considered verified if
//         * the {@link Result#verified} is {@code true}.
//         */
//        private static void verify(
//                DataSource apk,
//                long cdStartOffset,
//                List<CentralDirectoryRecord> cdRecords,
//                Set<String> cdEntryNames,
//                Map<Integer, String> supportedApkSigSchemeNames,
//                Set<Integer> foundApkSigSchemeIds,
//                int minSdkVersion,
//                int maxSdkVersion,
//                Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException {
//
//            // Find JAR manifest and signature block files.
//            CentralDirectoryRecord manifestEntry = null;
//            Map<String, CentralDirectoryRecord> sigFileEntries = new HashMap<>(1);
//            List<CentralDirectoryRecord> sigBlockEntries = new ArrayList<>(1);
//            for (CentralDirectoryRecord cdRecord : cdRecords) {
//                String entryName = cdRecord.getName();
//                if (!entryName.startsWith("META-INF/")) {
//                    continue;
//                }
//                if ((manifestEntry == null) && (MANIFEST_ENTRY_NAME.equals(entryName))) {
//                    manifestEntry = cdRecord;
//                    continue;
//                }
//                if (entryName.endsWith(".SF")) {
//                    sigFileEntries.put(entryName, cdRecord);
//                    continue;
//                }
//                if ((entryName.endsWith(".RSA"))
//                        || (entryName.endsWith(".DSA"))
//                        || (entryName.endsWith(".EC"))) {
//                    sigBlockEntries.add(cdRecord);
//                    continue;
//                }
//            }
//            if (manifestEntry == null) {
//                result.addError(Issue.JAR_SIG_NO_MANIFEST);
//                return;
//            }
//
//            // Parse the JAR manifest and check that all JAR entries it references exist in the APK.
//            byte[] manifestBytes;
//            try {
//                manifestBytes =
//                        LocalFileRecord.getUncompressedData(apk, manifestEntry, cdStartOffset);
//            } catch (ZipFormatException e) {
//                throw new ApkFormatException("Malformed ZIP entry: " + manifestEntry.getName(), e);
//            }
//            Map<String, ManifestParser.Section> entryNameToManifestSection = null;
//            ManifestParser manifest = new ManifestParser(manifestBytes);
//            ManifestParser.Section manifestMainSection = manifest.readSection();
//            List<ManifestParser.Section> manifestIndividualSections = manifest.readAllSections();
//            entryNameToManifestSection = new HashMap<>(manifestIndividualSections.size());
//            int manifestSectionNumber = 0;
//            for (ManifestParser.Section manifestSection : manifestIndividualSections) {
//                manifestSectionNumber++;
//                String entryName = manifestSection.getName();
//                if (entryName == null) {
//                    result.addError(Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION, manifestSectionNumber);
//                    continue;
//                }
//                if (entryNameToManifestSection.put(entryName, manifestSection) != null) {
//                    result.addError(Issue.JAR_SIG_DUPLICATE_MANIFEST_SECTION, entryName);
//                    continue;
//                }
//                if (!cdEntryNames.contains(entryName)) {
//                    result.addError(
//                            Issue.JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST, entryName);
//                    continue;
//                }
//            }
//            if (result.containsErrors()) {
//                return;
//            }
//            // STATE OF AFFAIRS:
//            // * All JAR entries listed in JAR manifest are present in the APK.
//
//            // Identify signers
//            List<Signer> signers = new ArrayList<>(sigBlockEntries.size());
//            for (CentralDirectoryRecord sigBlockEntry : sigBlockEntries) {
//                String sigBlockEntryName = sigBlockEntry.getName();
//                int extensionDelimiterIndex = sigBlockEntryName.lastIndexOf('.');
//                if (extensionDelimiterIndex == -1) {
//                    throw new RuntimeException(
//                            "Signature block file name does not contain extension: "
//                                    + sigBlockEntryName);
//                }
//                String sigFileEntryName =
//                        sigBlockEntryName.substring(0, extensionDelimiterIndex) + ".SF";
//                CentralDirectoryRecord sigFileEntry = sigFileEntries.get(sigFileEntryName);
//                if (sigFileEntry == null) {
//                    result.addWarning(
//                            Issue.JAR_SIG_MISSING_FILE, sigBlockEntryName, sigFileEntryName);
//                    continue;
//                }
//                String signerName = sigBlockEntryName.substring("META-INF/".length());
//                Result.SignerInfo signerInfo =
//                        new Result.SignerInfo(
//                                signerName, sigBlockEntryName, sigFileEntry.getName());
//                Signer signer = new Signer(signerName, sigBlockEntry, sigFileEntry, signerInfo);
//                signers.add(signer);
//            }
//            if (signers.isEmpty()) {
//                result.addError(Issue.JAR_SIG_NO_SIGNATURES);
//                return;
//            }
//
//            // Verify each signer's signature block file .(RSA|DSA|EC) against the corresponding
//            // signature file .SF. Any error encountered for any signer terminates verification, to
//            // mimic Android's behavior.
//            for (Signer signer : signers) {
//                signer.verifySigBlockAgainstSigFile(
//                        apk, cdStartOffset, minSdkVersion, maxSdkVersion);
//                if (signer.getResult().containsErrors()) {
//                    result.signers.add(signer.getResult());
//                }
//            }
//            if (result.containsErrors()) {
//                return;
//            }
//            // STATE OF AFFAIRS:
//            // * All JAR entries listed in JAR manifest are present in the APK.
//            // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
//
//            // Verify each signer's signature file (.SF) against the JAR manifest.
//            List<Signer> remainingSigners = new ArrayList<>(signers.size());
//            for (Signer signer : signers) {
//                signer.verifySigFileAgainstManifest(
//                        manifestBytes,
//                        manifestMainSection,
//                        entryNameToManifestSection,
//                        supportedApkSigSchemeNames,
//                        foundApkSigSchemeIds,
//                        minSdkVersion,
//                        maxSdkVersion);
//                if (signer.isIgnored()) {
//                    result.ignoredSigners.add(signer.getResult());
//                } else {
//                    if (signer.getResult().containsErrors()) {
//                        result.signers.add(signer.getResult());
//                    } else {
//                        remainingSigners.add(signer);
//                    }
//                }
//            }
//            if (result.containsErrors()) {
//                return;
//            }
//            signers = remainingSigners;
//            if (signers.isEmpty()) {
//                result.addError(Issue.JAR_SIG_NO_SIGNATURES);
//                return;
//            }
//            // STATE OF AFFAIRS:
//            // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
//            // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
//            // * All JAR entries listed in JAR manifest are present in the APK.
//
//            // Verify data of JAR entries against JAR manifest and .SF files. On Android, an APK's
//            // JAR entry is considered signed by signers associated with an .SF file iff the entry
//            // is mentioned in the .SF file and the entry's digest(s) mentioned in the JAR manifest
//            // match theentry's uncompressed data. Android requires that all such JAR entries are
//            // signed by the same set of signers. This set may be smaller than the set of signers
//            // we've identified so far.
//            Set<Signer> apkSigners =
//                    verifyJarEntriesAgainstManifestAndSigners(
//                            apk,
//                            cdStartOffset,
//                            cdRecords,
//                            entryNameToManifestSection,
//                            signers,
//                            minSdkVersion,
//                            maxSdkVersion,
//                            result);
//            if (result.containsErrors()) {
//                return;
//            }
//            // STATE OF AFFAIRS:
//            // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
//            // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
//            // * All JAR entries listed in JAR manifest are present in the APK.
//            // * All JAR entries present in the APK and supposed to be covered by JAR signature
//            //   (i.e., reside outside of META-INF/) are covered by signatures from the same set
//            //   of signers.
//
//            // Report any JAR entries which aren't covered by signature.
//            Set<String> signatureEntryNames = new HashSet<>(1 + result.signers.size() * 2);
//            signatureEntryNames.add(manifestEntry.getName());
//            for (Signer signer : apkSigners) {
//                signatureEntryNames.add(signer.getSignatureBlockEntryName());
//                signatureEntryNames.add(signer.getSignatureFileEntryName());
//            }
//            for (CentralDirectoryRecord cdRecord : cdRecords) {
//                String entryName = cdRecord.getName();
//                if ((entryName.startsWith("META-INF/"))
//                        && (!entryName.endsWith("/"))
//                        && (!signatureEntryNames.contains(entryName))) {
//                    result.addWarning(Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY, entryName);
//                }
//            }
//
//            // Reflect the sets of used signers and ignored signers in the result.
//            for (Signer signer : signers) {
//                if (apkSigners.contains(signer)) {
//                    result.signers.add(signer.getResult());
//                } else {
//                    result.ignoredSigners.add(signer.getResult());
//                }
//            }
//
//            result.verified = true;
//        }
//    }
//
//    private static class Signer {
//        private final String mName;
//        private final Result.SignerInfo mResult;
//        private final CentralDirectoryRecord mSignatureFileEntry;
//        private final CentralDirectoryRecord mSignatureBlockEntry;
//        private boolean mIgnored;
//
//        private byte[] mSigFileBytes;
//        private Set<String> mSigFileEntryNames;
//
//        private Signer(
//                String name,
//                CentralDirectoryRecord sigBlockEntry,
//                CentralDirectoryRecord sigFileEntry,
//                Result.SignerInfo result) {
//            mName = name;
//            mResult = result;
//            mSignatureBlockEntry = sigBlockEntry;
//            mSignatureFileEntry = sigFileEntry;
//        }
//
//        public String getName() {
//            return mName;
//        }
//
//        public String getSignatureFileEntryName() {
//            return mSignatureFileEntry.getName();
//        }
//
//        public String getSignatureBlockEntryName() {
//            return mSignatureBlockEntry.getName();
//        }
//
//        void setIgnored() {
//            mIgnored = true;
//        }
//
//        public boolean isIgnored() {
//            return mIgnored;
//        }
//
//        public Set<String> getSigFileEntryNames() {
//            return mSigFileEntryNames;
//        }
//
//        public Result.SignerInfo getResult() {
//            return mResult;
//        }
//
//        @SuppressWarnings("restriction")
//        public void verifySigBlockAgainstSigFile(
//                DataSource apk, long cdStartOffset, int minSdkVersion, int maxSdkVersion)
//                        throws IOException, ApkFormatException, NoSuchAlgorithmException {
//            byte[] sigBlockBytes;
//            try {
//                sigBlockBytes =
//                        LocalFileRecord.getUncompressedData(
//                                apk, mSignatureBlockEntry, cdStartOffset);
//            } catch (ZipFormatException e) {
//                throw new ApkFormatException(
//                        "Malformed ZIP entry: " + mSignatureBlockEntry.getName(), e);
//            }
//            try {
//                mSigFileBytes =
//                        LocalFileRecord.getUncompressedData(
//                                apk, mSignatureFileEntry, cdStartOffset);
//            } catch (ZipFormatException e) {
//                throw new ApkFormatException(
//                        "Malformed ZIP entry: " + mSignatureFileEntry.getName(), e);
//            }
//            PKCS7 sigBlock;
//            try {
//                sigBlock = new PKCS7(sigBlockBytes);
//            } catch (IOException e) {
//                if (e.getCause() instanceof CertificateException) {
//                    mResult.addError(
//                            Issue.JAR_SIG_MALFORMED_CERTIFICATE, mSignatureBlockEntry.getName(), e);
//                } else {
//                    mResult.addError(
//                            Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
//                }
//                return;
//            }
//            SignerInfo[] unverifiedSignerInfos = sigBlock.getSignerInfos();
//            if ((unverifiedSignerInfos == null) || (unverifiedSignerInfos.length == 0)) {
//                mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName());
//                return;
//            }
//
//            SignerInfo verifiedSignerInfo = null;
//            if ((unverifiedSignerInfos != null) && (unverifiedSignerInfos.length > 0)) {
//                for (int i = 0; i < unverifiedSignerInfos.length; i++) {
//                    SignerInfo unverifiedSignerInfo = unverifiedSignerInfos[i];
//                    String digestAlgorithmOid =
//                            unverifiedSignerInfo.getDigestAlgorithmId().getOID().toString();
//                    String signatureAlgorithmOid =
//                            unverifiedSignerInfo
//                                    .getDigestEncryptionAlgorithmId().getOID().toString();
//                    InclusiveIntRange desiredApiLevels =
//                            InclusiveIntRange.fromTo(minSdkVersion, maxSdkVersion);
//                    List<InclusiveIntRange> apiLevelsWhereDigestAndSigAlgorithmSupported =
//                            getSigAlgSupportedApiLevels(digestAlgorithmOid, signatureAlgorithmOid);
//                    List<InclusiveIntRange> apiLevelsWhereDigestAlgorithmNotSupported =
//                            desiredApiLevels.getValuesNotIn(apiLevelsWhereDigestAndSigAlgorithmSupported);
//                    if (!apiLevelsWhereDigestAlgorithmNotSupported.isEmpty()) {
//                        mResult.addError(
//                                Issue.JAR_SIG_UNSUPPORTED_SIG_ALG,
//                                mSignatureBlockEntry.getName(),
//                                digestAlgorithmOid,
//                                signatureAlgorithmOid,
//                                String.valueOf(apiLevelsWhereDigestAlgorithmNotSupported));
//                        return;
//                    }
//                    try {
//                        verifiedSignerInfo = sigBlock.verify(unverifiedSignerInfo, mSigFileBytes);
//                    } catch (SignatureException e) {
//                        mResult.addError(
//                                Issue.JAR_SIG_VERIFY_EXCEPTION,
//                                mSignatureBlockEntry.getName(),
//                                mSignatureFileEntry.getName(),
//                                e);
//                        return;
//                    }
//                    if (verifiedSignerInfo != null) {
//                        // Verified
//                        break;
//                    }
//
//                    // Did not verify
//                    if (minSdkVersion < AndroidSdkVersion.N) {
//                        // Prior to N, Android attempted to verify only the first SignerInfo.
//                        mResult.addError(
//                                Issue.JAR_SIG_DID_NOT_VERIFY,
//                                mSignatureBlockEntry.getName(),
//                                mSignatureFileEntry.getName());
//                        return;
//                    }
//                }
//            }
//            if (verifiedSignerInfo == null) {
//                mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName());
//                return;
//            }
//
//            // TODO: PKCS7 class doesn't guarantee that returned certificates' getEncoded returns
//            // the original encoded form of certificates rather than the DER re-encoded form. We
//            // need to replace the PKCS7 parser/verifier.
//            List<X509Certificate> certChain;
//            try {
//                certChain = verifiedSignerInfo.getCertificateChain(sigBlock);
//            } catch (IOException e) {
//                throw new RuntimeException(
//                        "Failed to obtain cert chain from " + mSignatureBlockEntry.getName(), e);
//            }
//            if ((certChain == null) || (certChain.isEmpty())) {
//                throw new RuntimeException("Verified SignerInfo does not have a certificate chain");
//            }
//            mResult.certChain.clear();
//            mResult.certChain.addAll(certChain);
//        }
//
//        private static final String OID_DIGEST_MD5 = "1.2.840.113549.2.5";
//        private static final String OID_DIGEST_SHA1 = "1.3.14.3.2.26";
//        private static final String OID_DIGEST_SHA224 = "2.16.840.1.101.3.4.2.4";
//        private static final String OID_DIGEST_SHA256 = "2.16.840.1.101.3.4.2.1";
//        private static final String OID_DIGEST_SHA384 = "2.16.840.1.101.3.4.2.2";
//        private static final String OID_DIGEST_SHA512 = "2.16.840.1.101.3.4.2.3";
//
//        private static final String OID_SIG_RSA = "1.2.840.113549.1.1.1";
//        private static final String OID_SIG_MD5_WITH_RSA = "1.2.840.113549.1.1.4";
//        private static final String OID_SIG_SHA1_WITH_RSA = "1.2.840.113549.1.1.5";
//        private static final String OID_SIG_SHA224_WITH_RSA = "1.2.840.113549.1.1.14";
//        private static final String OID_SIG_SHA256_WITH_RSA = "1.2.840.113549.1.1.11";
//        private static final String OID_SIG_SHA384_WITH_RSA = "1.2.840.113549.1.1.12";
//        private static final String OID_SIG_SHA512_WITH_RSA = "1.2.840.113549.1.1.13";
//
//        private static final String OID_SIG_DSA = "1.2.840.10040.4.1";
//        private static final String OID_SIG_SHA1_WITH_DSA = "1.2.840.10040.4.3";
//        private static final String OID_SIG_SHA224_WITH_DSA = "2.16.840.1.101.3.4.3.1";
//        private static final String OID_SIG_SHA256_WITH_DSA = "2.16.840.1.101.3.4.3.2";
//
//        private static final String OID_SIG_EC_PUBLIC_KEY = "1.2.840.10045.2.1";
//        private static final String OID_SIG_SHA1_WITH_ECDSA = "1.2.840.10045.4.1";
//        private static final String OID_SIG_SHA224_WITH_ECDSA = "1.2.840.10045.4.3.1";
//        private static final String OID_SIG_SHA256_WITH_ECDSA = "1.2.840.10045.4.3.2";
//        private static final String OID_SIG_SHA384_WITH_ECDSA = "1.2.840.10045.4.3.3";
//        private static final String OID_SIG_SHA512_WITH_ECDSA = "1.2.840.10045.4.3.4";
//
//        private static final Map<String, List<InclusiveIntRange>> SUPPORTED_SIG_ALG_OIDS =
//                new HashMap<>();
//        {
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_RSA,
//                    InclusiveIntRange.from(0));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_MD5_WITH_RSA,
//                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA1_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA224_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA256_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA384_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA512_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_RSA,
//                    InclusiveIntRange.from(0));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_MD5_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_RSA,
//                    InclusiveIntRange.from(0));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_RSA,
//                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_MD5_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_RSA,
//                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_RSA,
//                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_MD5_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_RSA,
//                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_RSA,
//                    InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_MD5_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_RSA,
//                    InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_RSA,
//                    InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_MD5_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_RSA,
//                    InclusiveIntRange.fromTo(21, 21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_RSA,
//                    InclusiveIntRange.from(21));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA1_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA224_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA256_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_DSA,
//                    InclusiveIntRange.from(0));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_DSA,
//                    InclusiveIntRange.from(9));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_DSA,
//                    InclusiveIntRange.from(22));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_DSA,
//                    InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_DSA,
//                    InclusiveIntRange.from(22));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_DSA,
//                    InclusiveIntRange.from(21));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_DSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_EC_PUBLIC_KEY,
//                    InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_EC_PUBLIC_KEY,
//                    InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_EC_PUBLIC_KEY,
//                    InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_EC_PUBLIC_KEY,
//                    InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_EC_PUBLIC_KEY,
//                    InclusiveIntRange.from(18));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA1_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA224_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA256_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA384_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_MD5, OID_SIG_SHA512_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_ECDSA,
//                    InclusiveIntRange.from(18));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_ECDSA,
//                    InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_ECDSA,
//                    InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_ECDSA,
//                    InclusiveIntRange.from(21));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_ECDSA,
//                    InclusiveIntRange.fromTo(21, 23));
//            addSupportedSigAlg(
//                    OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_ECDSA,
//                    InclusiveIntRange.from(21));
//        }
//
//        private static void addSupportedSigAlg(
//                String digestAlgorithmOid,
//                String signatureAlgorithmOid,
//                InclusiveIntRange... supportedApiLevels) {
//            SUPPORTED_SIG_ALG_OIDS.put(
//                    digestAlgorithmOid + "with" + signatureAlgorithmOid,
//                    Arrays.asList(supportedApiLevels));
//        }
//
//        private List<InclusiveIntRange> getSigAlgSupportedApiLevels(
//                String digestAlgorithmOid,
//                String signatureAlgorithmOid) {
//            List<InclusiveIntRange> result =
//                    SUPPORTED_SIG_ALG_OIDS.get(digestAlgorithmOid + "with" + signatureAlgorithmOid);
//            return (result != null) ? result : Collections.emptyList();
//        }
//
//        public void verifySigFileAgainstManifest(
//                byte[] manifestBytes,
//                ManifestParser.Section manifestMainSection,
//                Map<String, ManifestParser.Section> entryNameToManifestSection,
//                Map<Integer, String> supportedApkSigSchemeNames,
//                Set<Integer> foundApkSigSchemeIds,
//                int minSdkVersion,
//                int maxSdkVersion) throws NoSuchAlgorithmException {
//            // Inspect the main section of the .SF file.
//            ManifestParser sf = new ManifestParser(mSigFileBytes);
//            ManifestParser.Section sfMainSection = sf.readSection();
//            if (sfMainSection.getAttributeValue(Attributes.Name.SIGNATURE_VERSION) == null) {
//                mResult.addError(
//                        Issue.JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE,
//                        mSignatureFileEntry.getName());
//                setIgnored();
//                return;
//            }
//
//            if (maxSdkVersion >= AndroidSdkVersion.N) {
//                // Android N and newer rejects APKs whose .SF file says they were supposed to be
//                // signed with APK Signature Scheme v2 (or newer) and yet no such signature was
//                // found.
//                checkForStrippedApkSignatures(
//                        sfMainSection, supportedApkSigSchemeNames, foundApkSigSchemeIds);
//                if (mResult.containsErrors()) {
//                    return;
//                }
//            }
//
//            boolean createdBySigntool = false;
//            String createdBy = sfMainSection.getAttributeValue("Created-By");
//            if (createdBy != null) {
//                createdBySigntool = createdBy.indexOf("signtool") != -1;
//            }
//            boolean manifestDigestVerified =
//                    verifyManifestDigest(
//                            sfMainSection,
//                            createdBySigntool,
//                            manifestBytes,
//                            minSdkVersion,
//                            maxSdkVersion);
//            if (!createdBySigntool) {
//                verifyManifestMainSectionDigest(
//                        sfMainSection,
//                        manifestMainSection,
//                        manifestBytes,
//                        minSdkVersion,
//                        maxSdkVersion);
//            }
//            if (mResult.containsErrors()) {
//                return;
//            }
//
//            // Inspect per-entry sections of .SF file. Technically, if the digest of JAR manifest
//            // verifies, per-entry sections should be ignored. However, most Android platform
//            // implementations require that such sections exist.
//            List<ManifestParser.Section> sfSections = sf.readAllSections();
//            Set<String> sfEntryNames = new HashSet<>(sfSections.size());
//            int sfSectionNumber = 0;
//            for (ManifestParser.Section sfSection : sfSections) {
//                sfSectionNumber++;
//                String entryName = sfSection.getName();
//                if (entryName == null) {
//                    mResult.addError(
//                            Issue.JAR_SIG_UNNNAMED_SIG_FILE_SECTION,
//                            mSignatureFileEntry.getName(),
//                            sfSectionNumber);
//                    setIgnored();
//                    return;
//                }
//                if (!sfEntryNames.add(entryName)) {
//                    mResult.addError(
//                            Issue.JAR_SIG_DUPLICATE_SIG_FILE_SECTION,
//                            mSignatureFileEntry.getName(),
//                            entryName);
//                    setIgnored();
//                    return;
//                }
//                if (manifestDigestVerified) {
//                    // No need to verify this entry's corresponding JAR manifest entry because the
//                    // JAR manifest verifies in full.
//                    continue;
//                }
//                // Whole-file digest of JAR manifest hasn't been verified. Thus, we need to verify
//                // the digest of the JAR manifest section corresponding to this .SF section.
//                ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
//                if (manifestSection == null) {
//                    mResult.addError(
//                            Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
//                            entryName,
//                            mSignatureFileEntry.getName());
//                    setIgnored();
//                    continue;
//                }
//                verifyManifestIndividualSectionDigest(
//                        sfSection,
//                        createdBySigntool,
//                        manifestSection,
//                        manifestBytes,
//                        minSdkVersion,
//                        maxSdkVersion);
//            }
//            mSigFileEntryNames = sfEntryNames;
//        }
//
//
//        /**
//         * Returns {@code true} if the whole-file digest of the manifest against the main section of
//         * the .SF file.
//         */
//        private boolean verifyManifestDigest(
//                ManifestParser.Section sfMainSection,
//                boolean createdBySigntool,
//                byte[] manifestBytes,
//                int minSdkVersion,
//                int maxSdkVersion) throws NoSuchAlgorithmException {
//            Collection<NamedDigest> expectedDigests =
//                    getDigestsToVerify(
//                            sfMainSection,
//                            ((createdBySigntool) ? "-Digest" : "-Digest-Manifest"),
//                            minSdkVersion,
//                            maxSdkVersion);
//            boolean digestFound = !expectedDigests.isEmpty();
//            if (!digestFound) {
//                mResult.addWarning(
//                        Issue.JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE,
//                        mSignatureFileEntry.getName());
//                return false;
//            }
//
//            boolean verified = true;
//            for (NamedDigest expectedDigest : expectedDigests) {
//                String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
//                byte[] actual = digest(jcaDigestAlgorithm, manifestBytes);
//                byte[] expected = expectedDigest.digest;
//                if (!Arrays.equals(expected, actual)) {
//                    mResult.addWarning(
//                            Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
//                            V1SchemeSigner.MANIFEST_ENTRY_NAME,
//                            jcaDigestAlgorithm,
//                            mSignatureFileEntry.getName(),
//                            Base64.getEncoder().encodeToString(actual),
//                            Base64.getEncoder().encodeToString(expected));
//                    verified = false;
//                }
//            }
//            return verified;
//        }
//
//        /**
//         * Verifies the digest of the manifest's main section against the main section of the .SF
//         * file.
//         */
//        private void verifyManifestMainSectionDigest(
//                ManifestParser.Section sfMainSection,
//                ManifestParser.Section manifestMainSection,
//                byte[] manifestBytes,
//                int minSdkVersion,
//                int maxSdkVersion) throws NoSuchAlgorithmException {
//            Collection<NamedDigest> expectedDigests =
//                    getDigestsToVerify(
//                            sfMainSection,
//                            "-Digest-Manifest-Main-Attributes",
//                            minSdkVersion,
//                            maxSdkVersion);
//            if (expectedDigests.isEmpty()) {
//                return;
//            }
//
//            for (NamedDigest expectedDigest : expectedDigests) {
//                String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
//                byte[] actual =
//                        digest(
//                                jcaDigestAlgorithm,
//                                manifestBytes,
//                                manifestMainSection.getStartOffset(),
//                                manifestMainSection.getSizeBytes());
//                byte[] expected = expectedDigest.digest;
//                if (!Arrays.equals(expected, actual)) {
//                    mResult.addError(
//                            Issue.JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY,
//                            jcaDigestAlgorithm,
//                            mSignatureFileEntry.getName(),
//                            Base64.getEncoder().encodeToString(actual),
//                            Base64.getEncoder().encodeToString(expected));
//                }
//            }
//        }
//
//        /**
//         * Verifies the digest of the manifest's individual section against the corresponding
//         * individual section of the .SF file.
//         */
//        private void verifyManifestIndividualSectionDigest(
//                ManifestParser.Section sfIndividualSection,
//                boolean createdBySigntool,
//                ManifestParser.Section manifestIndividualSection,
//                byte[] manifestBytes,
//                int minSdkVersion,
//                int maxSdkVersion) throws NoSuchAlgorithmException {
//            String entryName = sfIndividualSection.getName();
//            Collection<NamedDigest> expectedDigests =
//                    getDigestsToVerify(
//                            sfIndividualSection, "-Digest", minSdkVersion, maxSdkVersion);
//            if (expectedDigests.isEmpty()) {
//                mResult.addError(
//                        Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
//                        entryName,
//                        mSignatureFileEntry.getName());
//                return;
//            }
//
//            int sectionStartIndex = manifestIndividualSection.getStartOffset();
//            int sectionSizeBytes = manifestIndividualSection.getSizeBytes();
//            if (createdBySigntool) {
//                int sectionEndIndex = sectionStartIndex + sectionSizeBytes;
//                if ((manifestBytes[sectionEndIndex - 1] == '\n')
//                        && (manifestBytes[sectionEndIndex - 2] == '\n')) {
//                    sectionSizeBytes--;
//                }
//            }
//            for (NamedDigest expectedDigest : expectedDigests) {
//                String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
//                byte[] actual =
//                        digest(
//                                jcaDigestAlgorithm,
//                                manifestBytes,
//                                sectionStartIndex,
//                                sectionSizeBytes);
//                byte[] expected = expectedDigest.digest;
//                if (!Arrays.equals(expected, actual)) {
//                    mResult.addError(
//                            Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY,
//                            entryName,
//                            jcaDigestAlgorithm,
//                            mSignatureFileEntry.getName(),
//                            Base64.getEncoder().encodeToString(actual),
//                            Base64.getEncoder().encodeToString(expected));
//                }
//            }
//        }
//
//        private void checkForStrippedApkSignatures(
//                ManifestParser.Section sfMainSection,
//                Map<Integer, String> supportedApkSigSchemeNames,
//                Set<Integer> foundApkSigSchemeIds) {
//            String signedWithApkSchemes =
//                    sfMainSection.getAttributeValue(
//                            V1SchemeSigner.SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR);
//            // This field contains a comma-separated list of APK signature scheme IDs which were
//            // used to sign this APK. Android rejects APKs where an ID is known to the platform but
//            // the APK didn't verify using that scheme.
//
//            if (signedWithApkSchemes == null) {
//                // APK signature (e.g., v2 scheme) stripping protections not enabled.
//                if (!foundApkSigSchemeIds.isEmpty()) {
//                    // APK is signed with an APK signature scheme such as v2 scheme.
//                    mResult.addWarning(
//                            Issue.JAR_SIG_NO_APK_SIG_STRIP_PROTECTION,
//                            mSignatureFileEntry.getName());
//                }
//                return;
//            }
//
//            if (supportedApkSigSchemeNames.isEmpty()) {
//                return;
//            }
//
//            Set<Integer> supportedApkSigSchemeIds = supportedApkSigSchemeNames.keySet();
//            Set<Integer> supportedExpectedApkSigSchemeIds = new HashSet<>(1);
//            StringTokenizer tokenizer = new StringTokenizer(signedWithApkSchemes, ",");
//            while (tokenizer.hasMoreTokens()) {
//                String idText = tokenizer.nextToken().trim();
//                if (idText.isEmpty()) {
//                    continue;
//                }
//                int id;
//                try {
//                    id = Integer.parseInt(idText);
//                } catch (Exception ignored) {
//                    continue;
//                }
//                // This APK was supposed to be signed with the APK signature scheme having
//                // this ID.
//                if (supportedApkSigSchemeIds.contains(id)) {
//                    supportedExpectedApkSigSchemeIds.add(id);
//                } else {
//                    mResult.addWarning(
//                            Issue.JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID,
//                            mSignatureFileEntry.getName(),
//                            id);
//                }
//            }
//
//            for (int id : supportedExpectedApkSigSchemeIds) {
//                if (!foundApkSigSchemeIds.contains(id)) {
//                    String apkSigSchemeName = supportedApkSigSchemeNames.get(id);
//                    mResult.addError(
//                            Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED,
//                            mSignatureFileEntry.getName(),
//                            id,
//                            apkSigSchemeName);
//                }
//            }
//        }
//    }
//
//    private static Collection<NamedDigest> getDigestsToVerify(
//            ManifestParser.Section section,
//            String digestAttrSuffix,
//            int minSdkVersion,
//            int maxSdkVersion) {
//        Decoder base64Decoder = Base64.getDecoder();
//        List<NamedDigest> result = new ArrayList<>(1);
//        if (minSdkVersion < AndroidSdkVersion.JELLY_BEAN_MR2) {
//            // Prior to JB MR2, Android platform's logic for picking a digest algorithm to verify is
//            // to rely on the ancient Digest-Algorithms attribute which contains
//            // whitespace-separated list of digest algorithms (defaulting to SHA-1) to try. The
//            // first digest attribute (with supported digest algorithm) found using the list is
//            // used.
//            String algs = section.getAttributeValue("Digest-Algorithms");
//            if (algs == null) {
//                algs = "SHA SHA1";
//            }
//            StringTokenizer tokens = new StringTokenizer(algs);
//            while (tokens.hasMoreTokens()) {
//                String alg = tokens.nextToken();
//                String attrName = alg + digestAttrSuffix;
//                String digestBase64 = section.getAttributeValue(attrName);
//                if (digestBase64 == null) {
//                    // Attribute not found
//                    continue;
//                }
//                alg = getCanonicalJcaMessageDigestAlgorithm(alg);
//                if ((alg == null)
//                        || (getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(alg)
//                                > minSdkVersion)) {
//                    // Unsupported digest algorithm
//                    continue;
//                }
//                // Supported digest algorithm
//                result.add(new NamedDigest(alg, base64Decoder.decode(digestBase64)));
//                break;
//            }
//            // No supported digests found -- this will fail to verify on pre-JB MR2 Androids.
//            if (result.isEmpty()) {
//                return result;
//            }
//        }
//
//        if (maxSdkVersion >= AndroidSdkVersion.JELLY_BEAN_MR2) {
//            // On JB MR2 and newer, Android platform picks the strongest algorithm out of:
//            // SHA-512, SHA-384, SHA-256, SHA-1.
//            for (String alg : JB_MR2_AND_NEWER_DIGEST_ALGS) {
//                String attrName = getJarDigestAttributeName(alg, digestAttrSuffix);
//                String digestBase64 = section.getAttributeValue(attrName);
//                if (digestBase64 == null) {
//                    // Attribute not found
//                    continue;
//                }
//                byte[] digest = base64Decoder.decode(digestBase64);
//                byte[] digestInResult = getDigest(result, alg);
//                if ((digestInResult == null) || (!Arrays.equals(digestInResult, digest))) {
//                    result.add(new NamedDigest(alg, digest));
//                }
//                break;
//            }
//        }
//
//        return result;
//    }
//
//    private static final String[] JB_MR2_AND_NEWER_DIGEST_ALGS = {
//            "SHA-512",
//            "SHA-384",
//            "SHA-256",
//            "SHA-1",
//    };
//
//    private static String getCanonicalJcaMessageDigestAlgorithm(String algorithm) {
//        return UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.get(algorithm.toUpperCase(Locale.US));
//    }
//
//    public static int getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(
//            String jcaAlgorithmName) {
//        Integer result =
//                MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.get(
//                        jcaAlgorithmName.toUpperCase(Locale.US));
//        return (result != null) ? result : Integer.MAX_VALUE;
//    }
//
//    private static String getJarDigestAttributeName(
//            String jcaDigestAlgorithm, String attrNameSuffix) {
//        if ("SHA-1".equalsIgnoreCase(jcaDigestAlgorithm)) {
//            return "SHA1" + attrNameSuffix;
//        } else {
//            return jcaDigestAlgorithm + attrNameSuffix;
//        }
//    }
//
//    private static final Map<String, String> UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL;
//    static {
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL = new HashMap<>(8);
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("MD5", "MD5");
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA", "SHA-1");
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA1", "SHA-1");
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-1", "SHA-1");
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-256", "SHA-256");
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-384", "SHA-384");
//        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-512", "SHA-512");
//    }
//
//    private static final Map<String, Integer>
//            MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST;
//    static {
//        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST = new HashMap<>(5);
//        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("MD5", 0);
//        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-1", 0);
//        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-256", 0);
//        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
//                "SHA-384", AndroidSdkVersion.GINGERBREAD);
//        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
//                "SHA-512", AndroidSdkVersion.GINGERBREAD);
//    }
//
//    private static byte[] getDigest(Collection<NamedDigest> digests, String jcaDigestAlgorithm) {
//        for (NamedDigest digest : digests) {
//            if (digest.jcaDigestAlgorithm.equalsIgnoreCase(jcaDigestAlgorithm)) {
//                return digest.digest;
//            }
//        }
//        return null;
//    }
//
//    public static List<CentralDirectoryRecord> parseZipCentralDirectory(
//            DataSource apk,
//            ApkUtils.ZipSections apkSections)
//                    throws IOException, ApkFormatException {
//        // Read the ZIP Central Directory
//        long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes();
//        if (cdSizeBytes > Integer.MAX_VALUE) {
//            throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes);
//        }
//        long cdOffset = apkSections.getZipCentralDirectoryOffset();
//        ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes);
//        cd.order(ByteOrder.LITTLE_ENDIAN);
//
//        // Parse the ZIP Central Directory
//        int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount();
//        List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount);
//        for (int i = 0; i < expectedCdRecordCount; i++) {
//            CentralDirectoryRecord cdRecord;
//            int offsetInsideCd = cd.position();
//            try {
//                cdRecord = CentralDirectoryRecord.getRecord(cd);
//            } catch (ZipFormatException e) {
//                throw new ApkFormatException(
//                        "Malformed ZIP Central Directory record #" + (i + 1)
//                                + " at file offset " + (cdOffset + offsetInsideCd),
//                        e);
//            }
//            String entryName = cdRecord.getName();
//            if (entryName.endsWith("/")) {
//                // Ignore directory entries
//                continue;
//            }
//            cdRecords.add(cdRecord);
//        }
//        // There may be more data in Central Directory, but we don't warn or throw because Android
//        // ignores unused CD data.
//
//        return cdRecords;
//    }
//
//    /**
//     * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's
//     * manifest for the APK to verify on Android.
//     */
//    private static boolean isJarEntryDigestNeededInManifest(String entryName) {
//        // NOTE: This logic is different from what's required by the JAR signing scheme. This is
//        // because Android's APK verification logic differs from that spec. In particular, JAR
//        // signing spec includes into JAR manifest all files in subdirectories of META-INF and
//        // any files inside META-INF not related to signatures.
//        if (entryName.startsWith("META-INF/")) {
//            return false;
//        }
//        return !entryName.endsWith("/");
//    }
//
//    private static Set<Signer> verifyJarEntriesAgainstManifestAndSigners(
//            DataSource apk,
//            long cdOffsetInApk,
//            Collection<CentralDirectoryRecord> cdRecords,
//            Map<String, ManifestParser.Section> entryNameToManifestSection,
//            List<Signer> signers,
//            int minSdkVersion,
//            int maxSdkVersion,
//            Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException {
//        // Iterate over APK contents as sequentially as possible to improve performance.
//        List<CentralDirectoryRecord> cdRecordsSortedByLocalFileHeaderOffset =
//                new ArrayList<>(cdRecords);
//        Collections.sort(
//                cdRecordsSortedByLocalFileHeaderOffset,
//                CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
//        Set<String> manifestEntryNamesMissingFromApk =
//                new HashSet<>(entryNameToManifestSection.keySet());
//        List<Signer> firstSignedEntrySigners = null;
//        String firstSignedEntryName = null;
//        for (CentralDirectoryRecord cdRecord : cdRecordsSortedByLocalFileHeaderOffset) {
//            String entryName = cdRecord.getName();
//            manifestEntryNamesMissingFromApk.remove(entryName);
//            if (!isJarEntryDigestNeededInManifest(entryName)) {
//                continue;
//            }
//
//            ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
//            if (manifestSection == null) {
//                result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
//                continue;
//            }
//
//            List<Signer> entrySigners = new ArrayList<>(signers.size());
//            for (Signer signer : signers) {
//                if (signer.getSigFileEntryNames().contains(entryName)) {
//                    entrySigners.add(signer);
//                }
//            }
//            if (entrySigners.isEmpty()) {
//                result.addError(Issue.JAR_SIG_ZIP_ENTRY_NOT_SIGNED, entryName);
//                continue;
//            }
//            if (firstSignedEntrySigners == null) {
//                firstSignedEntrySigners = entrySigners;
//                firstSignedEntryName = entryName;
//            } else if (!entrySigners.equals(firstSignedEntrySigners)) {
//                result.addError(
//                        Issue.JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH,
//                        firstSignedEntryName,
//                        getSignerNames(firstSignedEntrySigners),
//                        entryName,
//                        getSignerNames(entrySigners));
//                continue;
//            }
//
//            Collection<NamedDigest> expectedDigests =
//                    getDigestsToVerify(manifestSection, "-Digest", minSdkVersion, maxSdkVersion);
//            if (expectedDigests.isEmpty()) {
//                result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
//                continue;
//            }
//
//            MessageDigest[] mds = new MessageDigest[expectedDigests.size()];
//            int mdIndex = 0;
//            for (NamedDigest expectedDigest : expectedDigests) {
//                mds[mdIndex] = getMessageDigest(expectedDigest.jcaDigestAlgorithm);
//                mdIndex++;
//            }
//
//            try {
//                LocalFileRecord.outputUncompressedData(
//                        apk,
//                        cdRecord,
//                        cdOffsetInApk,
//                        new MessageDigestSink(mds));
//            } catch (ZipFormatException e) {
//                throw new ApkFormatException("Malformed ZIP entry: " + entryName, e);
//            } catch (IOException e) {
//                throw new IOException("Failed to read entry: " + entryName, e);
//            }
//
//            mdIndex = 0;
//            for (NamedDigest expectedDigest : expectedDigests) {
//                byte[] actualDigest = mds[mdIndex].digest();
//                if (!Arrays.equals(expectedDigest.digest, actualDigest)) {
//                    result.addError(
//                            Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
//                            entryName,
//                            expectedDigest.jcaDigestAlgorithm,
//                            V1SchemeSigner.MANIFEST_ENTRY_NAME,
//                            Base64.getEncoder().encodeToString(actualDigest),
//                            Base64.getEncoder().encodeToString(expectedDigest.digest));
//                }
//            }
//        }
//
//        if (firstSignedEntrySigners == null) {
//            result.addError(Issue.JAR_SIG_NO_SIGNED_ZIP_ENTRIES);
//            return Collections.emptySet();
//        } else {
//            return new HashSet<>(firstSignedEntrySigners);
//        }
//    }
//
//    private static List<String> getSignerNames(List<Signer> signers) {
//        if (signers.isEmpty()) {
//            return Collections.emptyList();
//        }
//        List<String> result = new ArrayList<>(signers.size());
//        for (Signer signer : signers) {
//            result.add(signer.getName());
//        }
//        return result;
//    }
//
//    private static MessageDigest getMessageDigest(String algorithm)
//            throws NoSuchAlgorithmException {
//        return MessageDigest.getInstance(algorithm);
//    }
//
//    private static byte[] digest(String algorithm, byte[] data, int offset, int length)
//            throws NoSuchAlgorithmException {
//        MessageDigest md = getMessageDigest(algorithm);
//        md.update(data, offset, length);
//        return md.digest();
//    }
//
//    private static byte[] digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
//        return getMessageDigest(algorithm).digest(data);
//    }
//
//    private static class NamedDigest {
//        private final String jcaDigestAlgorithm;
//        private final byte[] digest;
//
//        private NamedDigest(String jcaDigestAlgorithm, byte[] digest) {
//            this.jcaDigestAlgorithm = jcaDigestAlgorithm;
//            this.digest = digest;
//        }
//    }
//
//    public static class Result {
//
//        /** Whether the APK's JAR signature verifies. */
//        public boolean verified;
//
//        /** List of APK's signers. These signers are used by Android. */
//        public final List<SignerInfo> signers = new ArrayList<>();
//
//        /**
//         * Signers encountered in the APK but not included in the set of the APK's signers. These
//         * signers are ignored by Android.
//         */
//        public final List<SignerInfo> ignoredSigners = new ArrayList<>();
//
//        private final List<IssueWithParams> mWarnings = new ArrayList<>();
//        private final List<IssueWithParams> mErrors = new ArrayList<>();
//
//        private boolean containsErrors() {
//            if (!mErrors.isEmpty()) {
//                return true;
//            }
//            for (SignerInfo signer : signers) {
//                if (signer.containsErrors()) {
//                    return true;
//                }
//            }
//            return false;
//        }
//
//        private void addError(Issue msg, Object... parameters) {
//            mErrors.add(new IssueWithParams(msg, parameters));
//        }
//
//        private void addWarning(Issue msg, Object... parameters) {
//            mWarnings.add(new IssueWithParams(msg, parameters));
//        }
//
//        public List<IssueWithParams> getErrors() {
//            return mErrors;
//        }
//
//        public List<IssueWithParams> getWarnings() {
//            return mWarnings;
//        }
//
//        public static class SignerInfo {
//            public final String name;
//            public final String signatureFileName;
//            public final String signatureBlockFileName;
//            public final List<X509Certificate> certChain = new ArrayList<>();
//
//            private final List<IssueWithParams> mWarnings = new ArrayList<>();
//            private final List<IssueWithParams> mErrors = new ArrayList<>();
//
//            private SignerInfo(
//                    String name, String signatureBlockFileName, String signatureFileName) {
//                this.name = name;
//                this.signatureBlockFileName = signatureBlockFileName;
//                this.signatureFileName = signatureFileName;
//            }
//
//            private boolean containsErrors() {
//                return !mErrors.isEmpty();
//            }
//
//            private void addError(Issue msg, Object... parameters) {
//                mErrors.add(new IssueWithParams(msg, parameters));
//            }
//
//            private void addWarning(Issue msg, Object... parameters) {
//                mWarnings.add(new IssueWithParams(msg, parameters));
//            }
//
//            public List<IssueWithParams> getErrors() {
//                return mErrors;
//            }
//
//            public List<IssueWithParams> getWarnings() {
//                return mWarnings;
//            }
//        }
//    }
//}
